package main

import (
	"archive/tar"
	"archive/zip"
	"bufio"
	"crypto/md5"
	"crypto/sha1"
	_ "embed"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"runtime"
	"sort"
	"strconv"
	"strings"
	"time"

	// . "github.com/lxn/walk/declarative"
	"github.com/mholt/archiver"
	"github.com/nwaples/rardecode"
	"github.com/wonderivan/logger"

	// "github.com/gogf/gf/os/gfsnotify"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/widget"

	"github.com/flopp/go-findfont"
	//   "github.com/golang/freetype/truetype"
)

// 更新接口地址
// https://api-dev.mtywcloud.com/api-operate/software-products/upgrade?code=CloudPacsAssistTools&version=1.0.0.1
// https://api-dev.mtywcloud.com/api-operate/software-products/download?id=1454988718967361536
type LastVersion1 struct {
	Id       int    `json:"id"`
	Hashcode string `json:"hashcode"`
	Version  string `json:"ver"`
	Uptype   int    `json:"uptype"`
	Code     string `json:"code"`
	Downpath string
}

type ReturnLastVersion struct {
	Data []LastVersion `json:"data"`
	Code int           `json:"code"`
}

type MyProgressBar struct {
	allfileSize int64
	progressBar *widget.ProgressBar
	label       *widget.Label
	nowfileSize int64
}

type LastVersion struct {
	Id       string `json:"id"`
	Hashcode string `json:"file_hash"`
	Version  string `json:"version"`
	Uptype   bool   `json:"upgrade_state"`
	Downpath string
}

type MyConfig struct {
	Url             string `json:"url"`
	KTime           int    `json:"time"`
	Id              string `json:"id"`
	Hashcode        string `json:"hashcode"`
	Version         string `json:"version"`
	State           int    `json:"state"`
	Appname         string `json:"appname"`
	Downpath        string `json:"downpath"`
	NewId           string `json:"newid"`
	NewVersion      string `json:"newversion"`
	UpdatePath      string `json:"updatepath"`
	PreviousVersion string `json:"previousversion"`
	PreviousId      string `json:"previousid"`
	UpDateType      int    `json:"updatetype"` //更新类型，0为正向更新，1为回退
	ShowNew         int    `json:"shownew"`    //是否需要展示更新 1为需要
}

// 状态枚举
const (
	SATAT_UP_ERROR       = -4
	SATAT_DOWNFile_ERROR = -2
	//SATAT_RETURN_BACK    = -1
	SATAT_STAT         = 0
	SATAT_DOWNFile_ING = 1
	SATAT_DOWNFile_OK  = 2 //文件下载后，第三修改次程序  为waittoup
	SATAT_WAITTOUP     = 3 //次步骤为第三修改，本程序根据次状态进行解压，覆盖。覆盖的时候去掉配置文件
	SATAT_UP_ING       = 4
	SATAT_UPOK         = 5
)

const (
	//升级程序
	UPDATETYPE_UP = 0
	//回退
	UPDATEYPE_REBACK = 1
)

var myConfig MyConfig
var confgjson_path string
var lastver LastVersion
var nowVer LastVersion
var rundirPath string
var verList ReturnLastVersion
var myProgressBar MyProgressBar
var myApp fyne.App
var sleeptime int
var rebackpath string

type sortList []LastVersion

var code string = "ewAide"

//go:embed win.ico
var icon []byte

func GetNewVer() {
	if (myConfig.KTime < 5*60) && myConfig.KTime*sleeptime < 600 && sleeptime > 0 {
		sleeptime = sleeptime + 1
		return
	}
	sleeptime = 1
	if myConfig.State == SATAT_STAT || myConfig.State == SATAT_DOWNFile_ERROR {
		code = strings.Replace(myConfig.Appname, ".exe", "", 1)
		var verurl = strings.TrimRight(myConfig.Url, "/") + "/api-operate/software-products/upgrade?code=" + code + "&version="
		if myConfig.UpDateType == UPDATEYPE_REBACK {
			verurl = verurl + myConfig.PreviousVersion
		} else {
			verurl = verurl + lastver.Version
		}

		fmt.Printf("请求地址%s", verurl)
		path, err := url.PathUnescape(verurl)
		if err != nil {
			logger.Error("请求%s错误：%s", verurl, err)
			return
		}
		resp, err := http.Get(path)
		if err != nil {
			logger.Error("请求%s错误：%s", verurl, err)
			return
		}
		defer resp.Body.Close()
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			logger.Error("读取%s返回内存错误：%s", verurl)
			return
		}
		datajsond := []byte(body)
		fmt.Printf("返回内容%s", body)
		err = json.Unmarshal(datajsond, &verList)
		if err != nil {
			logger.Error("读取返回数据反序列错误：%s", err)
			return
		}
		if verList.Code == 0 && len(verList.Data) > 0 {
			f := verList.Data
			sort.Sort(sortList(f))
			lastver = f[len(f)-1]
			if myConfig.UpDateType == UPDATEYPE_REBACK {
				if len(f)-2 > -1 {
					lastver = f[len(f)-2]
				}
			}
			if len(lastver.Version) > 0 {
				myConfig.NewId = lastver.Id
				myConfig.NewVersion = lastver.Version
			}
		} else {
			fmt.Printf("返回内容错误%s", body)
		}
	}
}

func (I sortList) Len() int {
	return len(I)
}
func (I sortList) Less(i, j int) bool {
	return strings.Compare(I[i].Version, I[j].Version) < 0
}
func (I sortList) Swap(i, j int) {
	I[i], I[j] = I[j], I[i]
}

// 文件是否存在
func isPathExist(path string) bool {
	_, err := os.Stat(path)
	if err != nil {
		if os.IsExist(err) {
			return true
		}
		if os.IsNotExist(err) {
			return false
		}
		fmt.Println(err)
		return false
	}
	return true
}

// config.json文件不存在则创建
func createConfigJosn(bpath string) {
	myConfig.Hashcode = ""
	myConfig.Id = "0"
	myConfig.Version = "0.0.0.0"
	myConfig.State = SATAT_STAT
	myConfig.Url = ""
	myConfig.Appname = "ewAide.exe"
	myConfig.KTime = 600
	myConfig.PreviousVersion = "0.0.0.0"
	myConfig.UpDateType = 0
	myConfig.PreviousId = "0"
	ret_json, err := json.Marshal(myConfig)
	if err != nil {
		logger.Error("create config.json errror: -> %v", err)
	}
	ioutil.WriteFile(bpath, ret_json, 0777)

}

func ConfigInit() {
	rundirPath = GetBaseDirPath()
	confgjson_path = filepath.Join(rundirPath, "cconfig.json")
	fmt.Printf("配置文件地址：%s", confgjson_path)
	if !isPathExist(confgjson_path) {
		createConfigJosn(confgjson_path)
	} else {
		reLoadConfig()
	}
	nowVer.Hashcode = myConfig.Hashcode
	nowVer.Id = myConfig.Id
	nowVer.Version = myConfig.Version
	nowVer.Downpath = myConfig.Downpath
	fmt.Printf("lastver值:%v", lastver)
	lastver.Id = myConfig.NewId
	lastver.Version = myConfig.Version
}

func makeNewConfig(conf MyConfig) {
	if conf.Appname != myConfig.Appname {
		myConfig.Appname = conf.Appname
	}

	if conf.State != myConfig.State {
		myConfig.State = conf.State
	}
	if conf.Id != myConfig.Id {
		myConfig.Id = conf.Id
	}
	if conf.KTime != myConfig.KTime {
		myConfig.KTime = conf.KTime
	}
	if conf.Downpath != myConfig.Downpath {
		myConfig.Downpath = conf.Downpath
	}
	if conf.Version != myConfig.Version {
		myConfig.Version = conf.Version
	}
	if conf.Url != myConfig.Url {
		myConfig.Url = conf.Url
	}
	if conf.NewId != myConfig.NewId {
		myConfig.NewId = conf.NewId
	}
	if conf.NewVersion != myConfig.NewVersion {
		myConfig.NewVersion = conf.NewVersion
	}
	if conf.UpdatePath != myConfig.UpdatePath {
		myConfig.UpdatePath = conf.UpdatePath
	}

	if conf.PreviousVersion != myConfig.PreviousVersion {
		myConfig.PreviousVersion = conf.PreviousVersion
	}
	if conf.PreviousId != myConfig.PreviousId {
		myConfig.PreviousId = conf.PreviousId
	}
	if conf.UpDateType != myConfig.UpDateType {
		myConfig.UpDateType = conf.UpDateType
	}
}

func LoadConfig(filename string) (MyConfig, error) {
	var conf MyConfig
	data, err := ioutil.ReadFile(filename) //read config file
	if err != nil {
		return conf, err
	}
	datajsond := []byte(data)
	err = json.Unmarshal(datajsond, &conf)
	if err != nil {
		return conf, err
	}
	return conf, err
}

// 获取系统执行的目录
func GetBaseDirPath() string {
	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		fmt.Printf("获取配置文件路径错误，%s", err)
	}
	return dir
}

// 下载文件
func download() {
	downurl := fmt.Sprintf("%s/api-operate/software-products/download?id=%s", strings.TrimRight(myConfig.Url, "/"), lastver.Id)
	path, err := url.PathUnescape(downurl)
	res, err := http.Get(path)
	logger.Info("下载开始", path)
	if err != nil {
		logger.Error("http.Get -> %v", err)
		return
	}

	data, err := ioutil.ReadAll(res.Body)
	if err != nil {
		myConfig.State = SATAT_DOWNFile_ERROR
		logger.Error("下载错误：ioutil.ReadAll ->地址：%v,错误信息： %v", downurl, err.Error())
		return
	}
	f := ""
	for _, t := range res.Header {
		findex := strings.Index(t[0], "filename")
		if findex >= 0 {
			spstring := strings.Split(t[0], ";")
			for _, m := range spstring {
				if strings.Contains(m, "filename=") {
					spname := strings.Split(m, "=")
					f = spname[1]
				}
			}
		}
	}

	defer res.Body.Close()
	filename := lastver.Id + "_" + f
	filedir := filepath.Join(rundirPath, "down")
	if !isPathExist(filedir) {
		os.Mkdir(filedir, 0666)
	}
	var downfile = filepath.Join(filedir, filename)
	if err = ioutil.WriteFile(downfile, data, 0777); err != nil {
		logger.Error("Error Saving:", filename, err)
		myConfig.State = SATAT_DOWNFile_ERROR
	} else {
		if fmd5, err := GetFileSHA1(downfile); err == nil {
			if len(fmd5) > 0 { //== lastver.Hashcode  无法验证，取消验证
				logger.Info("Saved:", filename)
				//回退直接设置为等待更新
				if myConfig.UpDateType == UPDATEYPE_REBACK {
					myConfig.State = SATAT_WAITTOUP
				} else {
					myConfig.State = SATAT_DOWNFile_OK
				}
				myConfig.NewId = lastver.Id
				myConfig.Downpath = downfile
				nowVer.Downpath = downfile
			} else {
				myConfig.State = SATAT_DOWNFile_ERROR
				logger.Error("下载错误：下载的文件的sha1不一致%v,码：%v", downfile, fmd5)
			}

		} else {
			myConfig.State = SATAT_DOWNFile_ERROR
			logger.Error("下载错误：下载地址：%s,错误信息%v", downurl, err.Error())
		}
	}
	updateConfig()
}

func isProcessExist(appName string) (bool, string, int) {
	appary := make(map[string]int)
	cmd := exec.Command("cmd", "/C", "tasklist")
	output, _ := cmd.Output()
	//fmt.Printf("fields: %v\n", output)
	n := strings.Index(string(output), "System")
	if n == -1 {
		fmt.Println("no find")
		os.Exit(1)
	}
	data := string(output)[n:]
	fields := strings.Fields(data)
	for k, v := range fields {
		if v == appName {
			appary[appName], _ = strconv.Atoi(fields[k+1])
			return true, appName, appary[appName]
		}
	}
	return false, appName, -1
}

// func showprogress() {
// 	gtk.Init(&os.Args)

// 	win := gtk.NewWindow(gtk.WINDOW_TOPLEVEL)
// 	win.SetSizeRequest(400, 400)
// 	win.show()
// 	gtk.Main()

// }

func updateConfig() {
	if isPathExist(confgjson_path) {
		ret_json, err := json.Marshal(myConfig)
		if err == nil {
			ioutil.WriteFile(confgjson_path, ret_json, 0777)
		} else {
			fmt.Printf("更新 cconfig.json 错误：%s\r\n", err)
		}
	}
}

func GetFileMd5(path string) (string, error) {
	testFile := path
	if isPathExist(testFile) {
		fmt.Printf("%s\r\n", testFile)
		file, inerr := os.Open(testFile)
		if inerr == nil {
			md5h := md5.New()
			io.Copy(md5h, file)
			s := fmt.Sprintf("%x", md5h.Sum([]byte(""))) //md5
			return s, inerr
		} else {
			return "", inerr
		}
	} else {
		logger.Error(" 文件%s不存在", testFile)
		return "", fmt.Errorf("文件不存在")
	}
}

func GetFileSHA1(path string) (string, error) {
	testFile := path
	if isPathExist(testFile) {
		fmt.Printf("%s\r\n", testFile)
		file, inerr := os.Open(testFile)
		if inerr == nil {
			sha1h := sha1.New()
			io.Copy(sha1h, file)
			s := HexStringFromByteArray(sha1h.Sum([]byte("")), true, true) // fmt.Sprintf("%02x", sha1h.Sum([]byte(""))) //SHA1
			return s, inerr
		} else {
			return "", inerr
		}
	} else {
		logger.Error(" 文件%s不存在", testFile)
		return "", fmt.Errorf("文件不存在")
	}
}

func HexStringFromByteArray(bytes []byte, lowerCase bool, paddingZero bool) string {
	appendFormat := "%X"
	if paddingZero {
		appendFormat = "%02X"
	}

	var s strings.Builder
	for _, b := range bytes {
		s.WriteString(fmt.Sprintf(appendFormat, b))
	}
	result := s.String()
	if lowerCase {
		result = strings.ToLower(result)
	}
	return result
}

// 强制关闭进程
func killrunExe() error {
	//if myConfig.State == SATAT_WAITTOUP {
	fmt.Printf("   kill %s \r\n", myConfig.Appname)
	c := exec.Command("cmd.exe", "/C", "taskkill", "/F", "/IM", myConfig.Appname)
	err := c.Start()
	if err != nil {
		logger.Error("upload  kill %s server is error：%v\r\n", myConfig.Appname, err.Error())
		fmt.Printf("upload  kill %s server is error：%v\r\n", myConfig.Appname, err.Error())
	}
	c.Wait()
	return err
	//}
	//return nil
}

func toRebackfyne() {
	backpath := rebackpath
	if err := killrunExe(); err == nil {
		//备份
		toexe := rundirPath
		if len(myConfig.UpdatePath) > 0 {
			toexe = myConfig.UpdatePath
		}
		myProgressBar.label.SetText("检查备份文件")
		if isPathExist(backpath) {
			readDirSize(backpath)
			myProgressBar.label.SetText("版本回滚开始")
			if err := Copyfyne(backpath, toexe, "", "", true, myProgressBar.progressBar); err != nil {
				fmt.Printf("reback copy files to exepath is  error:%v\r\n", err)
				logger.Error("reback copy files to exepath is  error:%v\r\n", err)
			} else {
				myProgressBar.nowfileSize = myProgressBar.allfileSize
				updateProgressBar(myProgressBar.progressBar)
				myProgressBar.label.SetText("版本回滚完成")
				myConfig.State = SATAT_STAT
				myConfig.UpDateType = UPDATETYPE_UP
				myConfig.Id = myConfig.PreviousId
				myConfig.Version = myConfig.PreviousVersion
				myConfig.NewVersion = ""
				nowVer.Id = myConfig.Id
				nowVer.Version = myConfig.Version
				myConfig.Downpath = ""
				//不提供无限回滚
				myConfig.PreviousId = ""
				myConfig.PreviousVersion = ""
				myProgressBar.label.SetText("更新版本信息")
				updateConfig()
				myProgressBar.label.SetText("重新启动程序")
				ReStartEXE()
				if myApp != nil {
					myApp.Quit()
				}
			}
		} else {
			fmt.Printf("dir：%s not exists\r\n", backpath)
			logger.Error("dir%s is not exists %v\r\n", backpath, err)
		}

	} else {
		fmt.Printf("kill run exe error:%v", err)
	}
}

func toReback() {
	backpath := rebackpath
	if err := killrunExe(); err == nil {
		//备份
		toexe := rundirPath
		if len(myConfig.UpdatePath) > 0 {
			toexe = myConfig.UpdatePath
		}

		if isPathExist(backpath) {
			readDirSize(backpath)

			if err := Copy(backpath, toexe, "", "", false); err != nil {
				fmt.Printf("reback copy files to exepath is  error:%v\r\n", err)
				logger.Error("reback copy files to exepath is  error:%v\r\n", err)
			} else {

				myConfig.State = SATAT_STAT
				myConfig.UpDateType = UPDATETYPE_UP
				myConfig.Id = myConfig.PreviousId
				myConfig.Version = myConfig.PreviousVersion
				myConfig.NewVersion = ""
				nowVer.Id = myConfig.Id
				nowVer.Version = myConfig.Version
				myConfig.Downpath = ""
				//不提供无限回滚
				myConfig.PreviousId = ""
				myConfig.PreviousVersion = ""
				updateConfig()
				ReStartEXE()
			}
		} else {
			fmt.Printf("dir：%s not exists\r\n", backpath)
			logger.Error("dir%s is not exists %v\r\n", backpath, err)
		}

	} else {
		fmt.Printf("kill run exe error:%v", err)
	}
}

func updateTime(clock *widget.Label) {
	formatted := time.Now().Format("Time: 03:04:05")
	clock.SetText(clock.Text + "\r\n" + formatted)
}

func updateProgressBar(bar *widget.ProgressBar) {
	fmt.Printf("nowfileSize=%d,allfileSize=%d\r\n", myProgressBar.nowfileSize, myProgressBar.allfileSize)
	if myProgressBar.nowfileSize > 0 {
		i := float64(myProgressBar.nowfileSize) / float64(myProgressBar.allfileSize)
		fmt.Printf("bar=%d\r\n", i)
		bar.SetValue(i * 100)
	}
}

func toUpfyne(bar *widget.ProgressBar) {
	if err := killrunExe(); err == nil {
		//备份
		toexe := rundirPath
		if len(myConfig.UpdatePath) > 0 {
			toexe = myConfig.UpdatePath
		}
		//fmt.Printf("myProgressBar.allfileSize=%d\r\n",myProgressBar.allfileSize)
		go readDirSize(toexe)
		//	fmt.Printf("barksize=%d,myProgressBar.allfileSize=%d\r\n",barksize,myProgressBar.allfileSize)
		// if err==nil{
		// 	myProgressBar.allfileSize=barksize
		// }

		fmt.Printf("%d", myProgressBar.allfileSize)
		myProgressBar.label.SetText("备份数据")
		if err := Copyfyne(toexe, filepath.Join(rundirPath, "bark", nowVer.Version), "uploader,bark,down", "", false, bar); err == nil {
			//解压
			file := nowVer.Downpath
			if isPathExist(file) {
				to := strings.Replace(file, filepath.Ext(file), "", 1)
				fmt.Printf("解压缩目录：%s\r\n", to)
				myProgressBar.allfileSize = 0
				myProgressBar.nowfileSize = 0
				myProgressBar.label.SetText("解压安装包")
				updateProgressBar(bar)
				if err := DeCompress(file, to); err == nil {
					//fmt.Printf("myProgressBar.allfileSize=%d\r\n",myProgressBar.allfileSize)
					go readDirSize(to)
					// if err==nil{
					// 	myProgressBar.allfileSize=copysize
					// }
					myProgressBar.allfileSize = 0
					myProgressBar.nowfileSize = 0
					myProgressBar.label.SetText("升级程序")
					fmt.Printf("拷贝 from目录：%s,to目录：%s\r\n", to, toexe)
					if err := Copyfyne(to, toexe, "", ".json,.ini", true, bar); err != nil {
						logger.Error("升级错误，覆盖原文件失败：%v\r\n", err)
					} else {
						myProgressBar.nowfileSize = myProgressBar.allfileSize - 2000
						updateProgressBar(bar)
						myConfig.State = SATAT_STAT
						myConfig.PreviousId = myConfig.Id
						myConfig.PreviousVersion = myConfig.Version
						myConfig.Id = myConfig.NewId
						myConfig.Version = myConfig.NewVersion
						myConfig.NewVersion = ""
						nowVer.Id = myConfig.Id
						nowVer.Version = myConfig.Version
						myProgressBar.label.SetText("删除升级程序")
						os.RemoveAll(filepath.Join(rundirPath, "down"))
						myConfig.Downpath = ""
						myProgressBar.label.SetText("更新配置")
						updateConfig()
						myProgressBar.nowfileSize = myProgressBar.allfileSize
						updateProgressBar(bar)
						myProgressBar.label.SetText("完成升级，将自动启动程序。")
						ReStartEXE()
						if myApp != nil {
							myApp.Quit()
						}
					}
				} else {
					myConfig.State = SATAT_UP_ERROR
					logger.Error("解压%s失败:%v\r\n", file, err)
				}
			} else {
				myConfig.State = SATAT_UP_ERROR
				logger.Error("升级文件：%s不存在\r\n", file)
			}
		} else {
			myConfig.State = SATAT_UP_ERROR
			logger.Error("备份%s失败:%v\r\n", rundirPath, err)
		}
	} else {
		myConfig.State = SATAT_WAITTOUP
		logger.Error("备份%s失败:%v\r\n", rundirPath, err)
	}
}

func toUp() {
	if err := killrunExe(); err == nil {
		//备份
		toexe := rundirPath
		if len(myConfig.UpdatePath) > 0 {
			toexe = myConfig.UpdatePath
		}
		go readDirSize(toexe)
		if err := Copy(toexe, filepath.Join(rundirPath, "bark", nowVer.Version), "uploader,bark,down", "", false); err == nil {
			//解压
			file := nowVer.Downpath
			if isPathExist(file) {
				to := strings.Replace(file, filepath.Ext(file), "", 1)
				fmt.Printf("解压缩目录：%s\r\n", to)
				if err := DeCompress(file, to); err == nil {
					fmt.Printf("拷贝 from目录：%s,to目录：%s\r\n", to, toexe)
					if err := Copy(to, toexe, "", ".json,.ini", true); err != nil {
						logger.Error("升级错误，覆盖原文件失败：%v\r\n", err)
					} else {
						myConfig.State = SATAT_STAT
						myConfig.PreviousId = myConfig.Id
						myConfig.PreviousVersion = myConfig.Version
						myConfig.Id = myConfig.NewId
						myConfig.Version = myConfig.NewVersion
						myConfig.NewVersion = ""
						nowVer.Id = myConfig.Id
						nowVer.Version = myConfig.Version
						os.RemoveAll(filepath.Join(rundirPath, "down"))
						myConfig.Downpath = ""
						myConfig.ShowNew = 1
						updateConfig()
						ReStartEXE()
						if myApp != nil {
							myApp.Quit()
						}
					}
				} else {
					myConfig.State = SATAT_UP_ERROR
					logger.Error("解压%s失败:%v\r\n", file, err)
				}
			} else {
				myConfig.State = SATAT_UP_ERROR
				logger.Error("升级文件：%s不存在\r\n", file)
			}
		} else {
			myConfig.State = SATAT_UP_ERROR
			logger.Error("备份%s失败:%v\r\n", rundirPath, err)
		}
	} else {
		myConfig.State = SATAT_WAITTOUP
		logger.Error("备份%s失败:%v\r\n", rundirPath, err)
	}
}

func MakeUpload() {
	if myConfig.Url == "" {
		fmt.Printf("url地址没有配置，请先配置\r\n")
		logger.Error("url地址没有配置，请先配置")
		return
	}
	reLoadConfig()
	if myConfig.UpDateType == UPDATEYPE_REBACK {
		rebackVersion()
	} else {
		GetNewVer()
		fmt.Printf("现在id%s,最新id%s\r\n", nowVer.Version, lastver.Version)
		if strings.Compare(nowVer.Version, lastver.Version) < 0 && (myConfig.State == SATAT_STAT || myConfig.State == SATAT_DOWNFile_ERROR) {
			myConfig.State = SATAT_DOWNFile_ING
			download()
		}
		if myConfig.State == SATAT_WAITTOUP {
			myConfig.State = SATAT_UP_ING
			//toUp()
			//osTypeShow()
			toUp()
		}
	}
}

// 根据系统显示进度条，win7不支持显示，win10支持。
func osTypeShow() {
	fmt.Println(runtime.GOOS)
	Createfynewin("")
}

func rebackVersionByOnLine() {
	GetNewVer()
	if strings.Compare(nowVer.Version, lastver.Version) < 0 {
		download()
	}
}

func rebackVersion() {
	barklist := filepath.Join(rundirPath, "bark")
	fmt.Printf("目录：%s\r\n", barklist)
	lastfileInfo := GetDirByLastCreate(barklist, myConfig.PreviousVersion)
	//查询本地是否有备份，有备份就用本地回退
	if lastfileInfo.IsDir() {
		rebackpath = filepath.Join(barklist, lastfileInfo.Name())
		toReback()
		//Createfynewin("reback")
	} else { //没有本地备份使用网络进行回退
		//rebackVersionByOnLine()
		fmt.Printf("本地没有可以回退的版本，请重新下载新版本\r\n")
	}
}

func GetDirByLastCreate(dirstr string, version string) os.FileInfo {
	fileInfoList, err := ioutil.ReadDir(dirstr)
	var fileInfo os.FileInfo
	if err != nil {
		fmt.Printf("%v", err)
	}
	fmt.Println(len(fileInfoList))
	if len(fileInfoList) > 0 {
		fileInfo = fileInfoList[0]
		for i := range fileInfoList {
			if len(version) > 0 {
				fmt.Printf("文件名字:%s,版本：%s,是否相等%v\r\n", fileInfoList[i].Name(), version, strings.Contains(fileInfoList[i].Name(), version))
				if strings.Contains(fileInfoList[i].Name(), version) {
					fileInfo = fileInfoList[i]
					break
				}
			}
			if fileInfoList[i].ModTime().After(fileInfo.ModTime()) {
				fileInfo = fileInfoList[i]
			}
			//打印当前文件或目录下的文件或目录名
		}
	}
	return fileInfo
}

// 重新加载配置文件
func reLoadConfig() {
	conf, err := LoadConfig(confgjson_path) //get config struct
	if err != nil {
		logger.Error("load config.json errror: -> %v", err)
	} else {
		makeNewConfig(conf)
	}
}

func ReStartEXE() {
	runBasePath := rundirPath
	if len(myConfig.UpdatePath) > 0 {
		runBasePath = myConfig.UpdatePath
	}
	runPath := filepath.Join(runBasePath, myConfig.Appname)
	if isPathExist(runPath) {
		c := exec.Command("cmd.exe", "/c", "start "+runPath)
		if err := c.Run(); err != nil {
			fmt.Println("Error: ", err)
		}
	} else {
		fmt.Println("运行文件不存在: ", runPath)
	}
}

func compress(file *os.File, prefix string, zw *zip.Writer) error {
	info, err := file.Stat()
	if err != nil {
		return err
	}
	if info.IsDir() {
		prefix = filepath.Join(prefix, info.Name())
		fileInfos, err := file.Readdir(-1)
		if err != nil {
			return err
		}
		for _, fi := range fileInfos {
			f, err := os.Open(file.Name() + "/" + fi.Name())
			if err != nil {
				return err
			}
			err = compress(f, prefix, zw)
			if err != nil {
				return err
			}
		}
	} else {
		header, err := zip.FileInfoHeader(info)
		header.Name = prefix + "/" + header.Name
		if err != nil {
			return err
		}
		writer, err := zw.CreateHeader(header)
		if err != nil {
			return err
		}
		_, err = io.Copy(writer, file)
		file.Close()
		if err != nil {
			return err
		}
	}
	return nil
}

func DeCompress(souceFile, dest string) error {
	if strings.Contains(strings.ToLower(souceFile), ".zip") {
		return UnZipGz(souceFile, dest)
	}
	if strings.Contains(strings.ToLower(souceFile), ".tar") {
		return UnTarGz(souceFile, dest)
	}
	if strings.Contains(strings.ToLower(souceFile), ".rar") {
		return UnRar(souceFile, dest)
	}
	return nil
}

func UnRar(zipFile, dest string) error {
	r := archiver.NewRar()
	err := r.Walk(zipFile, func(f archiver.File) error {
		rh, ok := f.Header.(*rardecode.FileHeader)
		if !ok {
			return fmt.Errorf("expected header")
		}
		to := dest + rh.Name
		if !f.IsDir() && !r.OverwriteExisting && fileExists(to) {
			return fmt.Errorf("file already exists: %s", to)
		}

		// if files come before their containing folders, then we must
		// create their folders before writing the file
		err := mkdir(filepath.Dir(to))
		if err != nil {
			return fmt.Errorf("making parent directories: %v", err)
		}

		if f.IsDir() {
			return nil
		}

		return writeNewFile(to, f.ReadCloser, rh.Mode())
	})
	if err != nil {
		panic(err)
	}
	return err
}

func writeNewFile(fpath string, in io.Reader, fm os.FileMode) error {
	err := os.MkdirAll(filepath.Dir(fpath), 0755)
	if err != nil {
		return fmt.Errorf("%s: making directory for file: %v", fpath, err)
	}

	out, err := os.Create(fpath)
	if err != nil {
		return fmt.Errorf("%s: creating new file: %v", fpath, err)
	}
	defer out.Close()

	err = out.Chmod(fm)
	if err != nil && runtime.GOOS != "windows" {
		return fmt.Errorf("%s: changing file mode: %v", fpath, err)
	}

	_, err = io.Copy(out, in)
	if err != nil {
		return fmt.Errorf("%s: writing file: %v", fpath, err)
	}
	return nil
}

func fileExists(name string) bool {
	_, err := os.Stat(name)
	return !os.IsNotExist(err)
}

func mkdir(dirPath string) error {
	err := os.MkdirAll(dirPath, 0755)
	if err != nil {
		return fmt.Errorf("%s: making directory: %v", dirPath, err)
	}
	return nil
}

// 不能用
func UnpackRAR(zipFile, dest string) error {
	err := os.MkdirAll(dest, 0755)
	if err != nil {
		fmt.Printf("create dir error:%v", err)
		return err
	}
	var rarcmd = fmt.Sprintf("unrar x -ad -o+ %v %v ", dest, zipFile)
	if runtime.GOOS == "windows" {
		rarcmd = "winrar x -icbk -y  "
	}
	cmd := exec.Command(rarcmd, filepath.Join(dest), filepath.Join(zipFile))
	err = cmd.Run()
	if err != nil {
		return err
	}
	return nil
}

// 解压
func UnZipGz(zipFile, dest string) error {
	reader, err := zip.OpenReader(zipFile)
	if err != nil {
		return err
	}
	defer reader.Close()
	//log.Println("create dir MkdirAll:",getDir(dest))
	err = os.MkdirAll(dest, 0755)
	if err != nil {
		logger.Error("create dir error:%v", err)
		return err
	}
	for _, file := range reader.File {
		rc, err := file.Open()
		if err != nil {
			return err
		}
		defer rc.Close()
		filename := filepath.Join(dest, file.Name)
		// fmt.Printf("文件：%v \r\n", filename)
		// fmt.Printf("目录：%v\r\n", getDir(filename))
		// fmt.Printf("是否为目录：%v\r\n", file.FileInfo().IsDir())
		if file.FileInfo().IsDir() {
			err = os.MkdirAll(filename, 0755)
			if err != nil {
				return err
			}
			continue
		}
		err = os.MkdirAll(filepath.Dir(filename), 0755)
		if err != nil {
			return err
		}
		w, err := os.Create(filename)
		if err != nil {
			return err
		}
		defer w.Close()
		_, err = io.Copy(w, rc)
		if err != nil {
			return err
		}
		w.Close()
		rc.Close()
	}
	return nil
}

// 解包  可以解压，压缩文件里有文件夹的，也可以解压压缩文件里全是文件的
// srcGz待解包文件
// dstDir 解包之后存放的目录
func UnTarGz(srcGz string, dstDir string) error {
	fr, err := os.Open(srcGz)
	if err != nil {

		return err
	}
	defer fr.Close()
	fmt.Println("--- dstDir:", dstDir)
	dstDir = path.Clean(dstDir) + string(os.PathSeparator)
	fmt.Println("=== dstDir:", dstDir)
	err = os.MkdirAll(dstDir, 0755)
	if err != nil {
		logger.Error("create dir error:%v", err)
		return err
	}
	//打开压缩文件

	// gw, err := gzip.NewReader(fr)
	// defer gw.Close()
	// 创建 tar.Reader 对象，准备执行解包操作
	// 可以用 tr.Next() 来遍历包中的文件
	tr := tar.NewReader(fr)
	// 遍历包中的文件
	for hdr, er := tr.Next(); er != io.EOF; hdr, er = tr.Next() {
		if er != nil {
			fmt.Println("--------- er:", er)
			return er
		}
		// 获取文件信息
		fi := hdr.FileInfo()
		// 获取绝对路径
		dstFullPath := dstDir + hdr.Name
		fmt.Println("--- dstFullPath:", dstFullPath)
		if hdr.Typeflag == tar.TypeDir {
			fmt.Println("--- type is floder")
			// 创建目录
			os.MkdirAll(dstFullPath, fi.Mode().Perm())
			// 设置目录权限
			os.Chmod(dstFullPath, fi.Mode().Perm())
		} else {
			fmt.Println(dstFullPath, "--- type is file")
			// 创建文件所在的目录

			strtemp := dstFullPath[:strings.LastIndex(dstFullPath, "\\")+1]
			//os.MkdirAll(path.Dir(dstFullPath), os.ModePerm)

			os.MkdirAll(strtemp, os.ModePerm)
			// 将 tr 中的数据写入文件中
			if er := unTarFile(dstFullPath, tr); er != nil {
				fmt.Println(er)
				return er
			}
			// 设置文件权限
			os.Chmod(dstFullPath, fi.Mode().Perm())
		}
	}

	return nil
}

// 因为要在 defer 中关闭文件，所以要单独创建一个函数
func unTarFile(dstFile string, tr *tar.Reader) error {
	// 创建空文件，准备写入解包后的数据
	fw, er := os.Create(dstFile)
	if er != nil {
		return er
	}
	defer fw.Close()

	// 写入解包后的数据
	_, er = io.Copy(fw, tr)
	if er != nil {
		return er
	}

	return nil
}

// 复制整个文件夹或单个文件
func Copyfyne(from, to, notdir, ext string, istop bool, bar *widget.ProgressBar) error {
	f, e := os.Stat(from)
	if e != nil {
		return e
	}
	if f.IsDir() {
		if notdir != "" {
			t := strings.Split(notdir, ",")
			for _, k := range t {
				if strings.Contains(from, k) {
					return nil
				}
			}
		}
		//from是文件夹，那么定义to也是文件夹
		if list, e := ioutil.ReadDir(from); e == nil {
			for _, item := range list {
				t := filepath.Join(to, item.Name())
				if istop == true {
					t = to
				}
				if e = Copyfyne(filepath.Join(from, item.Name()), t, notdir, ext, false, bar); e != nil {
					return e
				}
			}
		}
	} else {
		fmt.Printf("from:%s,to:%s\r\n", from, to)
		//from是文件，那么创建to的文件夹
		if ext != "" {
			t := strings.Split(ext, ",")
			for _, k := range t {
				if strings.Contains(from, k) {
					if strings.Contains(to, k) {
						return nil
					}
				}
			}
		}
		if strings.Contains(from, "eWorldAutouploader.exe") || strings.Contains(from, "cconfig.json") {
			return nil
		}
		p := filepath.Dir(to)
		if _, e = os.Stat(p); e != nil {
			if e = os.MkdirAll(p, 0777); e != nil {
				return e
			}
		}
		myProgressBar.nowfileSize = myProgressBar.nowfileSize + getFileSize(from)
		go updateProgressBar(bar)
		//读取源文件
		file, e := os.Open(from)
		if e != nil {
			return e
		}

		defer file.Close()
		bufReader := bufio.NewReader(file)
		// 创建一个文件用于保存
		out, e := os.Create(to)
		if e != nil {
			return e
		}

		defer out.Close()
		// 然后将文件流和文件流对接起来
		_, e = io.Copy(out, bufReader)
	}
	return e
}

func Copy(from, to, notdir, ext string, istop bool) error {
	f, e := os.Stat(from)
	if e != nil {
		return e
	}
	if f.IsDir() {
		if notdir != "" {
			t := strings.Split(notdir, ",")
			for _, k := range t {
				if strings.Contains(from, k) {
					return nil
				}
			}
		}
		//from是文件夹，那么定义to也是文件夹
		if list, e := ioutil.ReadDir(from); e == nil {
			for _, item := range list {
				t := filepath.Join(to, item.Name())
				if istop == true {
					t = to
				}
				if e = Copy(filepath.Join(from, item.Name()), t, notdir, ext, false); e != nil {
					return e
				}
			}
		}
	} else {
		fmt.Printf("from:%s,to:%s\r\n", from, to)
		//from是文件，那么创建to的文件夹
		if ext != "" {
			t := strings.Split(ext, ",")
			for _, k := range t {
				if strings.Contains(from, k) {
					return nil
					// if strings.Contains(to, k) {

					// }
				}
			}
		}
		if strings.Contains(from, "eWorldAutouploader.exe") || strings.Contains(from, "cconfig.json") {
			return nil
		}
		p := filepath.Dir(to)
		if _, e = os.Stat(p); e != nil {
			if e = os.MkdirAll(p, 0777); e != nil {
				return e
			}
		}
		myProgressBar.nowfileSize = myProgressBar.nowfileSize + getFileSize(from)

		//读取源文件
		file, e := os.Open(from)
		if e != nil {
			return e
		}

		defer file.Close()
		bufReader := bufio.NewReader(file)
		// 创建一个文件用于保存
		out, e := os.Create(to)
		if e != nil {
			return e
		}

		defer out.Close()
		// 然后将文件流和文件流对接起来
		_, e = io.Copy(out, bufReader)
	}
	return e
}

// getFileSize get file size by path(B)  不能用
//
//	func DirSizeB(path string) (int64, error) {
//		var size int64
//		err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
//			if !info.IsDir() {
//				size += info.Size()
//			}
//			return err
//		})
//		return size, err
//	}
//
// 递归读取文件夹中的文件大小
func readDirSize(dirPath string) {
	flist, e := ioutil.ReadDir(dirPath)
	if e != nil {
		fmt.Println("read file error")
		return
	}
	for _, f := range flist {
		if f.IsDir() {
			readDirSize(dirPath + "/" + f.Name())
		} else {
			myProgressBar.allfileSize += f.Size()
		}
	}
	return
}

// getFileSize get file size by path(B)
func getFileSize(path string) int64 {
	if !exists(path) {
		return 0
	}
	fileInfo, err := os.Stat(path)
	if err != nil {
		return 0
	}
	return fileInfo.Size()
}

// exists Whether the path exists
func exists(path string) bool {
	_, err := os.Stat(path)
	return err == nil || os.IsExist(err)
}

func Createfynewin(b string) {
	res := fyne.NewStaticResource("icon", icon)
	// icon1:=widget.NewIcon(res);

	myApp = app.New()
	title := "版本升级"
	if b == "reback" {
		title = "版本回滚"
	}
	myWindow := myApp.NewWindow(title)
	myWindow.SetIcon(res)
	lbltitle := widget.NewLabel("")
	//	updateTime(clock)
	bar1 := widget.NewProgressBar()
	bar1.Min = 0
	bar1.Max = 100
	//text:=widget.NewRichTextFromMarkdown();
	//bar2 := widget.NewProgressBarInfinite()
	// go func() {
	// 	for i := 0; i <= 100; i++ {
	// 		time.Sleep(time.Millisecond * 500)
	// 		bar1.SetValue(float64(i))
	// 		//updateTime(clock)
	// 	}

	// }()
	if b == "reback" {
		go func() {
			toRebackfyne()
		}()
	} else {
		go func() {
			toUpfyne(bar1)
		}()
	}
	content := container.NewVBox(lbltitle, bar1)
	myProgressBar.progressBar = bar1
	myProgressBar.label = lbltitle
	myProgressBar.allfileSize = 0
	myProgressBar.nowfileSize = 0
	//myProgressBar.text=text
	myWindow.SetContent(content)

	myWindow.Resize(fyne.NewSize(600, 100))
	myWindow.CenterOnScreen()
	myWindow.SetPadded(true)
	myWindow.ShowAndRun()
}
func setcnfont() {
	fontPaths := findfont.List()
	for _, path := range fontPaths {
		//fmt.Println(path)
		//楷体:simkai.ttf
		//黑体:simhei.ttf
		if strings.Contains(path, "simkai.ttf") {
			os.Setenv("FYNE_FONT", path)
			break
		}
	}

}

func main() {
	//设置字体
	setcnfont()
	fmt.Printf("%s", rundirPath)
	ConfigInit()
	MakeUpload()
	str, _ := os.Getwd()
	fmt.Printf("%s", str)
	//Createfynewin()
	//定时 10分钟获取下版本信息
	if myConfig.KTime <= 0 {
		myConfig.KTime = 60
	}
	d := time.Second * time.Duration(myConfig.KTime)
	t1 := time.NewTimer(d)
	for {
		select {
		case <-t1.C:
			MakeUpload()
			t1.Reset(d)
		}
	}
	os.Unsetenv("FYNE_FONT")

}
